package sg.edu.nus.iss.billing.model.bill;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import sg.edu.nus.iss.billing.controller.BillController;
import sg.edu.nus.iss.billing.controller.GSTRateContrller;
import sg.edu.nus.iss.billing.exceptions.ControllerException;
import sg.edu.nus.iss.billing.model.BaseModel;
import sg.edu.nus.iss.billing.model.account.Account;
import sg.edu.nus.iss.billing.model.charge.IChargable;
import sg.edu.nus.iss.billing.model.company.Company;
import sg.edu.nus.iss.billing.model.subscription.ISubscription;

/**
 * @author Yeo Hee Ling, Ananda, EngKhoon
 */
public class Bill extends BaseModel {
	private static final long serialVersionUID = 1L;

	private Account billingAccount;
	private Company company;
	private BillingPeriod billingPeriod;
	private Map<ISubscription, List<IChargable>> subscriptionCharges;
	private Date billDate;
	private Date dueDate;
	private double totalGST;
	private double totalCurrCharges;

	public Bill(Account account, BillingPeriod period) {
		this.billingPeriod = period;
		this.billingAccount = account;
		this.subscriptionCharges = new HashMap<ISubscription, List<IChargable>>();
		this.populateCharges();
		this.calculateCharges();
	}

	public List<IChargable> getAllCharges() {
		List<IChargable> charges = new ArrayList<IChargable>();
		for (Map.Entry<ISubscription, List<IChargable>> entry : this.subscriptionCharges.entrySet())
			charges.addAll(entry.getValue());
		return charges;
	}

	public Date getBillDate() {
		return this.billDate;
	}

	public Account getBillingAccount() {
		return this.billingAccount;
	}

	public BillingPeriod getBillingPeriod() {
		return this.billingPeriod;
	}

	public Map<ISubscription, List<IChargable>> getCharges() {
		return this.subscriptionCharges;
	}

	public Company getCompany() {
		return this.company;
	}

	public Date getDueDate() {
		return this.dueDate;
	}

	public double getTotalCurrCharges() {
		return this.totalCurrCharges;
	}

	public double getTotalGST() {
		return this.totalGST;
	}

	public void setBillDate(Date billDate) {
		this.billDate = billDate;
	}

	public void setCharges(ISubscription subscription, List<IChargable> charges) {
		this.subscriptionCharges.put(subscription, charges);
	}

	public void setDueDate(Date dueDate) {
		this.dueDate = dueDate;
	}

	private void calculateCharges() {
		this.totalCurrCharges = this.calculateTotalCharges();
		this.totalGST = this.calculateTotalGST(this.totalCurrCharges);
	}

	private double calculateTotalCharges() {
		double totalCurrentCharges = 0.00;
		for (IChargable charge : this.getAllCharges())
			totalCurrentCharges = totalCurrentCharges + charge.charge(this);
		return Math.round(totalCurrentCharges * 100.00) / 100.00;// round decimal 2 point
	}

	private double calculateTotalGST(double totalCharges) {
		return totalCharges * (this.getGSTRate() / 100);
	}

	private double getGSTRate() {
		try {
			return GSTRateContrller.getCurrentRate(this.billingPeriod);
		} catch (ControllerException e) {
			e.printStackTrace();
			return 0.0;
		}
	}

	private void populateCharges() {
		if (this.billingAccount.getSubscriptions().size() > 0)
			for (ISubscription subscription : this.billingAccount.getSubscriptions())
				if (BillController.isSubscriptionRequiredToBeCharged(subscription, this.getBillingPeriod()))
					subscription.charge(this);
	}
}
